﻿using System;
using System.Collections;
using System.Collections.Generic;
using Pfz.Threading.Unsafe;

namespace Pfz.Threading
{
	/// <summary>
	/// This class is responsible for holding an UnsafeThreadedEnumerator.
	/// When disposed or collected it will release the thread used by such
	/// enumerator.
	/// </summary>
	public class ThreadedEnumerator<T>:
		DisposeAssurerBase<UnsafeThreadedEnumerator<T>>,
		IEnumerator<T>
	{
		/// <summary>
		/// Creates a new threaded enumerator from the given action.
		/// </summary>
		public static ThreadedEnumerator<T> CreateFromAction(EnumeratorAction<T> action)
		{
			return new _UnsafeThreadedEnumeratorFromAction<T>(action);
		}

		/// <summary>
		/// Creates a new ThreadedEnumerator from an unsafe one.
		/// </summary>
		protected ThreadedEnumerator(UnsafeThreadedEnumerator<T> unsafeEnumerator):
			base(unsafeEnumerator)
		{
			unsafeEnumerator._Start();
		}

		/// <summary>
		/// Implicit cast from UnsafeThreadedEnumerator to ThreadedEnumerator.
		/// </summary>
		public static implicit operator ThreadedEnumerator<T>(UnsafeThreadedEnumerator<T> unsafeEnumerator)
		{
			return new ThreadedEnumerator<T>(unsafeEnumerator);
		}

		/// <summary>
		/// Tells the enumerator to start the next step.
		/// </summary>
		public bool BeginUpdate()
		{
			var value = Value;
			if (value == null)
				throw new IteratorDisposedException(GetType().FullName);

			return value._BeginUpdate();
		}

		/// <summary>
		/// Wait until the next frame ends.
		/// </summary>
		public bool EndUpdate(out T result)
		{
			var value = Value;
			if (value == null)
				throw new IteratorDisposedException(GetType().FullName);

			return value._EndUpdate(out result);
		}

		/// <summary>
		/// Tells the enumerator to process the next step and wait for it.
		/// </summary>
		public bool Update(out T result)
		{
			var value = Value;
			if (value == null)
				throw new IteratorDisposedException(GetType().FullName);

			return value._Update(out result);
		}

		#region IEnumerator<T> Members
			private T _current;
			T IEnumerator<T>.Current
			{
				get
				{
					return _current;
				}
			}
		#endregion
		#region IEnumerator Members
			object IEnumerator.Current
			{
				get
				{
					return _current;
				}
			}

			bool IEnumerator.MoveNext()
			{
				return Update(out _current);
			}

			void IEnumerator.Reset()
			{
				throw new NotSupportedException("Threaded enumerators are not resetable.");
			}

		#endregion
	}
}
